'use strict'

/**
 * <--    还原前    -->
var a = [1, 2, 3, [1213, 234], {"code":"666"}, window];
var b = a[1]+a[2]+a[3];
var c = a[4];
var d = a[5];
 * 
 * <--    特点    --> 
 * 先定义一个数组，里面存着待引用的元素，每个元素都没有被修改过
 * 那么直接将后续的引用直接替换为元素本身，更易阅读
 * 
 * <--    解决办法    -->
 * 从数组声明路径进入，通过binding取得关联的引用路径，通过MemberExpression.property获取到相应数组成员element，做替换
 * 删除该数组声明
 * 
 * <--    冗余判断    -->
 * 1. 判断初始化值是否是数组类型
 * 2. 判断该数组是否被修改过
 * 3. 判断该数组成员是否被修改过
 * 4. 假设没有函数对该数组有过副作用的操作（以数组为参数，函数内部修改数组或成员）
 * 
 * <--    还原后    -->
var b = 2 + 3 + [1213, 234];
var c = {
  "code": "666"
};
var d = window;
 *
 **/


const {parse} = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const t = require('@babel/types');
const generator = require('@babel/generator').default;

let code = `var a = [1, 2, 3, [1213, 234], {"code":"666"}, window];
var b = a[1]+a[2]+a[3];
var c = a[4];
var d = a[5];`

const visitor = {
    VariableDeclarator:{
        enter: function(path){
            if(!t.isArrayExpression(path.get('init').node)){return}
            let name = path.get('id').node.name;
            if(!path.scope.getBinding(name).constant){return}

            // 检查数组成员有无变化(a[3] = 10)，或者数组有无调用方法改变成员(a.pop())
            for(let refPath of path.scope.getBinding(name).referencePaths){
                let parent = refPath.parentPath;
                let grandp = parent.parentPath;

                if(grandp &&
                    t.isAssignmentExpression(grandp.node) &&
                    grandp.get('left') === parent &&
                    t.isMemberExpression(parent.node) &&
                    parent.get('object') === refPath){
                        console.log('修改了元素赋值');
                        return
                    }
                
                if(grandp &&
                    t.isCallExpression(grandp.node) &&
                    grandp.get('callee') === parent &&
                    t.isMemberExpression(parent.node) &&
                    parent.get('object') === refPath){
                        console.log('发生了方法调用行为');
                        return
                    }
            }

            // 替换为成员字面量
            for(let refPath of path.scope.getBinding(name).referencePaths){
                let memberExpPath = refPath.parentPath;
                let idx = memberExpPath.get('property').node.value;
                // console.log(idx, typeof idx);
                // console.log(path.get('init.elements.'+idx).type);
                refPath.parentPath.replaceInline(path.get('init.elements.'+idx));
            }

            // 删除原数组绑定
            path.remove();
        },
    },
}

let ast = parse(code);

traverse(ast, visitor);
console.log(generator(ast).code);
